﻿using DeHeng_Mes.Business;
using DeHeng_Mes.Constant;
using DeHeng_Mes.Dao;
using DeHeng_Mes.Model;
using DeHeng_Mes.Model.Boss;
using DeHeng_Mes.Model.ConfigModel;
using DeHeng_Mes.Model.OrderSingleton;
using DeHeng_Mes.Model.PlcModel;
using DeHeng_Mes.Model.Table;
using Newtonsoft.Json;
using NPOI.SS.Formula.Functions;
using S7.Net;
using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Forms;
using RichTextBox = System.Windows.Forms.RichTextBox;

namespace DeHeng_Mes.Utils
{

    /// <summary>
    /// 常用工具类
    /// </summary>
    public static class Tools
    {

        #region  成员变量
        public static MainForm UiForm { get; set; }

        #endregion

        /// <summary>
        /// 跨线程操作
        /// </summary>
        /// <param name="uiForm"></param>
        public static void ToolControl(MainForm uiForm)
        {
            UiForm = uiForm;
        }


        /// <summary>
        /// 根据年月日 换算星期
        /// </summary>
        /// <param name="y">年</param>
        /// <param name="m">月</param>
        /// <param name="d">日</param>
        /// <returns></returns>
        public static String DateToWeek(int y, int m, int d)
        {
            if (m == 1) m = 13;
            if (m == 2) m = 14;
            int week = (d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400) % 7 + 1;
            string weekstr = "";
            switch (week)
            {
                case 1: weekstr = "星期一"; break;
                case 2: weekstr = "星期二"; break;
                case 3: weekstr = "星期三"; break;
                case 4: weekstr = "星期四"; break;
                case 5: weekstr = "星期五"; break;
                case 6: weekstr = "星期六"; break;
                case 7: weekstr = "星期日"; break;
            }
            return weekstr;
        }

        /// <summary>
        /// 根据指定的时间节点判断 白班/晚班 并返回
        /// </summary>
        public static String GetWorkType()
        {

            DateTime whiteShiftStart = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 8, 30, 0);
            DateTime whiteShiftEnd = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 20, 30, 0);

            if (DateTime.Now >= whiteShiftStart && DateTime.Now < whiteShiftEnd)
            {
                return "A"; // 白班  
            }
            else
            {
                return "B"; // 晚班  
            }
        }

        /// <summary>
        /// 根据年份 获取对应的代码
        /// </summary>
        /// <param name="year"></param>
        /// <returns></returns>
        public static String GetYearCodeByYear(int year)
        {

            switch (year)
            {
                case 2024:
                    return "R";
                case 2025:
                    return "S";
                case 2026:
                    return "T";
                case 2027:
                    return "V";
                case 2028:
                    return "W";
                case 2029:
                    return "X";
                case 2030:
                    return "Y";
                default:
                    return "";
            }
        }

        /// <summary>
        /// 根据月份 获取对应的代码
        /// </summary>
        /// <param name="year"></param>
        /// <returns></returns>
        public static String GetMonthCodeByMonth(int month)
        {

            if (month <= 9)
            {
                return month.ToString();
            }
            else
            {
                switch (month)
                {
                    case 10:
                        return "A";
                    case 11:
                        return "B";
                    case 12:
                        return "C";
                    default:
                        return "";
                }
            }
        }

        /// <summary>
        /// 根据天 获取对应的代码
        /// </summary>
        /// <param name="year"></param>
        /// <returns></returns>
        public static String GetDayCodeByDay(int day)
        {

            if (day <= 9)
            {
                return day.ToString();
            }
            else
            {
                switch (day)
                {
                    case 10:
                        return "A";
                    case 11:
                        return "B";
                    case 12:
                        return "C";
                    case 13:
                        return "D";
                    case 14:
                        return "E";
                    case 15:
                        return "F";
                    case 16:
                        return "G";
                    case 17:
                        return "H";
                    case 18:
                        return "J";
                    case 19:
                        return "K";
                    case 20:
                        return "L";
                    case 21:
                        return "M";
                    case 22:
                        return "N";
                    case 23:
                        return "P";
                    case 24:
                        return "R";
                    case 25:
                        return "S";
                    case 26:
                        return "T";
                    case 27:
                        return "V";
                    case 28:
                        return "W";
                    case 29:
                        return "X";
                    case 30:
                        return "Y";
                    case 31:
                        return "Z";
                    default:
                        return "";
                }
            }
        }

        /// <summary>
        /// 尝试将字符串转换为 日期格式
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static DateTime ParseCustomDateTime(string input)
        {
            // 尝试将字符串解析为 DateTime
            if (DateTime.TryParseExact(input, "yyyyMMddHHmmssfff", null, System.Globalization.DateTimeStyles.None, out DateTime result))
            {
                return result;
            }
            else
            {
                return DateTime.Now;
            }
        }


        /// <summary>
        /// 刷新Grid
        /// </summary>
        /// <param name="infoModels"></param>
        public static void RefreshDataGridView(DataGridView grid, Object infoModels, bool autoGenerateColumns = false)
        {
            //禁止自动生成列
            grid.AutoGenerateColumns = autoGenerateColumns;
            //清空grid
            grid.DataSource = null;
            //加载到grid
            grid.DataSource = infoModels;
            //刷新
            grid.Refresh();
        }

        /// <summary>
        /// 通过反射的方式将List转换为DataTable
        /// </summary>
        /// <typeparam name="T"> 泛型</typeparam>
        /// <param name="items">转换对象</param>
        /// <returns></returns>
        public static DataTable ConvertToDataTable<T>(List<T> items)
        {
            if (items == null)
                return null;
            DataTable dataTable = new DataTable(typeof(T).Name);

            // 获取对象的属性  
            PropertyInfo[] props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (PropertyInfo prop in props)
            {
                // 添加列到DataTable  
                Type colType = GetCoreType(prop.PropertyType);
                dataTable.Columns.Add(prop.Name, colType);
            }

            // 填充数据  
            foreach (T item in items)
            {
                var values = new object[props.Length];
                for (int i = 0; i < props.Length; i++)
                {
                    values[i] = props[i].GetValue(item, null);
                }
                dataTable.Rows.Add(values);
            }

            return dataTable;
        }

        /// <summary>
        /// 返回本周的开始和结束时间
        /// </summary>
        /// <returns></returns>
        public static (DateTime Start, DateTime End) GetWeekTimeRange()
        {
            // 获取当前日期时间  
            DateTime now = DateTime.Now;

            // 计算本周的开始时间（周一0点）  
            DateTime startOfWeek = now.Date.AddDays(-(int)now.DayOfWeek + (int)DayOfWeek.Monday);

            // 计算本周的结束时间（周日23:59:59）  
            DateTime endOfWeek = startOfWeek.AddDays(7).AddSeconds(-1); // 加7天到下周的周一，然后减去1秒  

            // 返回开始和结束时间  
            return (startOfWeek, endOfWeek);
        }

        /// <summary>
        /// 生产日志消息处理
        /// </summary>
        /// <param name="richTextBox"></param>
        /// <param name="message"></param>
        public static async void ProduceLogText(string message)
        {
            try
            {
                await AsyncLogger.WriteLogAsync("ProduceLog", message);

                if (UiForm != null && !UiForm.IsDisposed && UiForm.InvokeRequired && !CloseStatus.IsClose)
                {
                    UiForm.Invoke(new Action(() =>
                    {
                        ClearIfExceedMaxLine(UiForm.ProductionLogsText, 50000);
                        UiForm.ProductionLogsText.AppendText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "：" + message + "\n");
                    }));
                }
                else
                {
                    UiForm.ProductionLogsText.AppendText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "：" + message + "\n");
                }
            }
            catch (Exception e)
            {
                Console.Write(e);
            }
        }

        /// <summary>
        /// 设备运行日志消息处理
        /// </summary>
        /// <param name="message"></param>
        public static async void DeviceLogText(string message)
        {
            try
            {
                await AsyncLogger.WriteLogAsync("DeviceLog", message);

                if (UiForm != null && !UiForm.IsDisposed && UiForm.InvokeRequired && !CloseStatus.IsClose)
                {
                    UiForm.Invoke(new Action(() =>
                    {
                        ClearIfExceedMaxLine(UiForm.ProductionLogsText, 50000);
                        UiForm.DeviceLogsText.AppendText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "：" + message + "\n");
                    }));
                }
                else
                {
                    UiForm.DeviceLogsText.AppendText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "：" + message + "\n");
                }
            }
            catch (Exception e)
            {
                Console.Write(e);
            }
        }

        /// <summary>
        /// 生产日志消息报错处理
        /// </summary>
        /// <param name="richTextBox"></param>
        /// <param name="message"></param>
        /// <param name="color"></param>
        public static async void ProduceLogTextError(string message)
        {
            try
            {
                await AsyncLogger.WriteLogAsync("ProduceLog", message);

                //日志高亮
                if (UiForm != null && !UiForm.IsDisposed && UiForm.InvokeRequired && !CloseStatus.IsClose)
                {
                    UiForm.Invoke(new Action(() =>
                    {
                        ClearIfExceedMaxLine(UiForm.ProductionLogsText, 50000);
                        AppendLogMessage(UiForm.ProductionLogsText, message);
                    }));
                }
                else
                {
                    AppendLogMessage(UiForm.ProductionLogsText, message);
                }
            }
            catch (Exception e)
            {
                Console.Write(e);
            }
        }

        /// <summary>
        /// 设备日志消息报错处理
        /// </summary>
        /// <param name="richTextBox"></param>
        /// <param name="message"></param>
        /// <param name="color"></param>
        public static async void DeviceLogTextError(string message)
        {
            try
            {
                await AsyncLogger.WriteLogAsync("DeviceLog", message);

                //日志高亮
                if (UiForm != null && !UiForm.IsDisposed && UiForm.InvokeRequired && !CloseStatus.IsClose)
                {
                    UiForm.Invoke(new Action(() =>
                    {
                        ClearIfExceedMaxLine(UiForm.ProductionLogsText, 50000);
                        AppendLogMessage(UiForm.DeviceLogsText, message);
                    }));
                }
                else
                {
                    AppendLogMessage(UiForm.DeviceLogsText, message);
                }
            }
            catch (Exception e)
            {
                Console.Write(e);
            }
        }

        /// <summary>
        /// 控制白板输出字符
        /// </summary>
        /// <param name="richTextBox"></param>
        /// <param name="maxLines"></param>
        private static void ClearIfExceedMaxLine(RichTextBox richTextBox, int maxLines)
        {
            if (richTextBox.TextLength > maxLines)
            {
                richTextBox.Clear();
            }
        }

        /// <summary>
        /// 测试数据库连接
        /// </summary>
        /// <param name="richTextBox"></param>
        public static bool SqlConnect()
        {
            bool isOk = false;
            try
            {
                using (var db = SqlSugarConfig.Db)
                {
                    db.Ado.GetDataTable("SELECT 1");
                    ProduceLogText("数据库已经连接");
                    isOk = true;
                }
            }
            catch (Exception ex)
            {
                ProduceLogTextError("数据库连接失败，原因是：" + ex.Message);
            }
            return isOk;
        }

        /// <summary>
        /// 根据订单号查找该订单所有报警信息，并且根据时间筛选出报警的开始时间或结束时间在StartHour与EndHour间隔中的数据返回
        /// </summary>
        /// <param name="startHour"></param>
        /// <param name="endHour"></param>
        /// <param name="orderId"></param>
        /// <returns>返回报警信息的列表</returns>
        /*public static List<DeviceFault> ErrorMessageInTime(int startHour, int endHour, string operation)
        {
            DeviceDao deviceDao = new DeviceDao();
            List<DeviceFault> list = new List<DeviceFault>();
            // 先根据PlcName查询所有的数据
            List<DeviceFault> deviceFaults = deviceDao.QueryAll(operation);

            foreach (var item in deviceFaults)
            {
                // 获取报警开始和结束时间的小时数
                int startTimeHour = item.StopStartTime.Hour;

                // 如果结束时间为空，表示报警还未恢复
                if (item.StopEndTime == DateTime.MinValue)
                {
                    // 仅判断开始时间是否在指定范围内
                    if (startTimeHour <= endHour || startHour > endHour) // 处理跨午夜情况
                    {
                        list.Add(item);
                    }
                }
                else
                {
                    // 否则判断开始时间或结束时间是否在指定的时间范围内
                    int endTimeHour = item.StopEndTime.Hour;

                    // 处理跨午夜情况
                    bool isInTimeRange = (startHour <= endHour) ?
                        (startTimeHour >= startHour && startTimeHour <= endHour) ||
                        (endTimeHour >= startHour && endTimeHour <= endHour) :
                        (startTimeHour >= startHour || startTimeHour <= endHour) ||
                        (endTimeHour >= startHour || endTimeHour <= endHour);

                    if (isInTimeRange)
                    {
                        list.Add(item);
                    }
                }
            }

            return list;
        }*/



        /// <summary>
        /// 计算出当前小时内，所有报警信息的总时长，相同时间段内的报警互相覆盖而不是叠加
        /// </summary>
        /// <param name="deviceFaults">报警信息的列表</param>
        /// <param name="startHour">时间间隔开始时间</param>
        /// <param name="endHour">时间间隔结束时间</param>
        /// <returns>返回分钟</returns>
        public static string DateTimeCalculate(List<DeviceFault> deviceFaults, int startHour, int endHour)
        {
            List<(DateTime Start, DateTime End)> timeRanges = new List<(DateTime, DateTime)>();

            DateTime startOfDay = DateTime.Today.AddHours(startHour);
            DateTime endOfDay = startHour < endHour
                ? DateTime.Today.AddHours(endHour)
                : DateTime.Today.AddDays(1).AddHours(endHour); // 跨天时，结束时间为第二天的指定小时

            foreach (var fault in deviceFaults)
            {
                DateTime startTime = fault.StopStartTime;
                DateTime endTime = fault.StopEndTime == DateTime.MinValue ? DateTime.MaxValue : fault.StopEndTime;

                // 检查报警的开始和结束时间是否在指定范围内
                if (endTime > startOfDay && startTime < endOfDay)
                {
                    // 修正时间段
                    startTime = startTime < startOfDay ? startOfDay : startTime;
                    endTime = endTime > endOfDay ? endOfDay : endTime;

                    if (startTime < endTime)
                    {
                        timeRanges.Add((startTime, endTime));
                    }
                }
            }

            // 合并重叠的时间段
            timeRanges = MergeTimeRanges(timeRanges, startHour, endHour);

            // 计算合并后的总时长
            double totalMinutes = timeRanges.Sum(range => (range.End - range.Start).TotalMinutes);

            return totalMinutes.ToString("F0");
        }


        /// <summary>
        /// 合并重叠时间段
        /// </summary>
        /// <param name="timeRanges">要合并的时间段列表</param>
        /// <param name="startHour">时间段开始小时</param>
        /// <param name="endHour">时间段结束小时</param>
        /// <returns>合并后的时间段列表</returns>
        private static List<(DateTime Start, DateTime End)> MergeTimeRanges(List<(DateTime Start, DateTime End)> timeRanges, int startHour, int endHour)
        {
            if (!timeRanges.Any())
                return new List<(DateTime, DateTime)>();

            // 先按开始时间排序
            timeRanges.Sort((a, b) => a.Start.CompareTo(b.Start));
            List<(DateTime Start, DateTime End)> mergedRanges = new List<(DateTime, DateTime)>();
            var currentRange = timeRanges[0];

            foreach (var range in timeRanges.Skip(1))
            {
                // 检查是否有重叠
                if (range.Start <= currentRange.End)
                {
                    // 如果有重叠，合并时间段
                    currentRange = (currentRange.Start, new DateTime(Math.Max(currentRange.End.Ticks, range.End.Ticks)));
                }
                else
                {
                    // 添加不重叠的时间段并更新当前时间段
                    mergedRanges.Add(currentRange);
                    currentRange = range;
                }
            }
            mergedRanges.Add(currentRange);

            // 处理跨午夜的合并
            if (startHour > endHour) // 表示跨午夜的情况
            {
                // 对于合并后的时间段，检查跨午夜的情况
                for (int i = 0; i < mergedRanges.Count - 1; i++)
                {
                    // 如果当前范围结束时间跨过午夜，并且下一个范围开始时间也跨过了午夜
                    if (mergedRanges[i].End.TimeOfDay >= TimeSpan.FromHours(startHour) &&
                        mergedRanges[i + 1].Start.TimeOfDay <= TimeSpan.FromHours(endHour))
                    {
                        // 合并时间段
                        mergedRanges[i] = (mergedRanges[i].Start, mergedRanges[i + 1].End);
                        mergedRanges.RemoveAt(i + 1); // 移除下一个时间段
                        i--; // 调整索引
                    }
                }
            }

            return mergedRanges;
        }

        public static async Task<BossMessager> PostApiDataAsync(string Data,string Url)
        {
            using (HttpClient client = new HttpClient())
            {
                client.BaseAddress = new Uri(Url);

                StringContent content = new StringContent(Data, Encoding.UTF8, "application/json");

                try
                {
                    // 发送 POST 请求
                    HttpResponseMessage response = await client.PostAsync("data", content);

                    // 确保请求成功
                    response.EnsureSuccessStatusCode();

                    // 读取 JSON 响应内容
                    string responseData = await response.Content.ReadAsStringAsync();
                    Console.WriteLine("Response Data: " + responseData);

                    // 反序列化 JSON 数据
                    BossMessager result = JsonConvert.DeserializeObject<BossMessager>(responseData);
                    return result;
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Error: " + ex.Message);
                }
            }
            return null;
        }

        /// <summary>
        /// 将"0-1"格式转换成"00:00-01:00"格式
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string ConvertToTimeRange(string input)
        {
            string[] parts = input.Split('-');
            int startHour = int.Parse(parts[0]);
            int endHour = int.Parse(parts[1]);

            string startTime = startHour.ToString("D2") + ":00";
            string endTime = endHour.ToString("D2") + ":00";

            return $"{startTime}-{endTime}";
        }

        /// <summary>
        /// 将"00:00-01:00"格式转换成"0-1"格式
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string ConvertToHourRange(string input)
        {
            string[] parts = input.Split('-');
            int startHour = int.Parse(parts[0].Substring(0, 2));
            int endHour = int.Parse(parts[1].Substring(0, 2));

            return $"{startHour}-{endHour}";
        }

        /// <summary>
        /// 获取当前时间所在的小时段
        /// </summary>
        /// <param name="dateTimeStr"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static string GetTimeRangeFromDateTime(string dateTimeStr)
        {
            // 解析输入的时间字符串
            DateTime dateTime;
            if (!DateTime.TryParse(dateTimeStr, out dateTime))
            {
                throw new ArgumentException("输入的时间格式无效，请确保为有效的年月日时分秒格式");
            }

            // 获取小时数
            int startHour = dateTime.Hour;
            int endHour = (startHour + 1) % 24;

            // 格式化输出
            string startTime = startHour.ToString("D2") + ":00";
            string endTime = endHour.ToString("D2") + ":00";

            return $"{startTime}-{endTime}";
        }

        /// <summary>
        /// 通用方法：实体类转换成dic，key为属性名，value为属性值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entity"></param>
        /// <returns></returns>
        public static List<KeyValuePair<string, string>> ToPropertyDictionary<T>(this T entity) where T : class
        {
            List<KeyValuePair<string, string>> keyValueList = new List<KeyValuePair<string, string>>();

            if (entity == null)
                return keyValueList;

            // 获取所有公共实例属性
            PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);

            foreach (PropertyInfo property in properties)
            {
                if (property.CanRead)
                {
                    object value = property.GetValue(entity);

                    string stringValue = value?.ToString() ?? string.Empty;

                    keyValueList.Add(new KeyValuePair<String, String>(property.Name, stringValue));
                }
            }

            return keyValueList;
        }

        #region 其他内部方法

        /// <summary>
        /// 检查泛型的可空类型
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        private static Type GetCoreType(Type t)
        {
            if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                return Nullable.GetUnderlyingType(t);
            }
            return t;
        }

        /// <summary>
        /// 日志高亮处理
        /// </summary>
        /// <param name="richTextBox"></param>
        /// <param name="message"></param>
        public static void AppendLogMessage(RichTextBox richTextBox, string message)
        {
            try
            {
                message = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "：" + message;

                // 将消息添加到RichTextBox
                richTextBox.AppendText(message + Environment.NewLine);

                // 高亮显示刚添加的消息
                int startIndex = richTextBox.TextLength - message.Length - 1; // 计算消息的起始索引
                if (startIndex == -1)
                {
                    startIndex = 0;
                }

                richTextBox.Select(startIndex, message.Length);
                richTextBox.SelectionColor = Color.Red;
                richTextBox.SelectionFont = new Font(richTextBox.Font, FontStyle.Bold);

                // 清除选择
                richTextBox.Select(richTextBox.TextLength, 0);
                // 重置颜色为默认
                richTextBox.SelectionColor = richTextBox.ForeColor;
                richTextBox.SelectionFont = richTextBox.Font;
            }
            catch (Exception e) { 
                Tools.ProduceLogTextError($"高亮日志记录错误，原因是{e}");
            } 
            }

            /// <summary>
            /// 处理字符串转换为Plc可以接受的类型
            /// </summary>
            /// <param name="input"></param>
            /// <param name="maxLength"></param>
            /// <returns></returns>
        public static byte[] ConvertToPlcString(string input, int maxLength)
        {
            byte[] plcString = new byte[maxLength + 2]; // 预留2字节给最大长度和实际长度
            plcString[0] = (byte)(maxLength); // 设置最大长度
            plcString[1] = (byte)Math.Min(input.Length, maxLength); // 设置实际长度

            byte[] stringBytes = Encoding.ASCII.GetBytes(input);
            Array.Copy(stringBytes, 0, plcString, 2, plcString[1]);
            return plcString;
        }

        /// <summary>
        /// 根据工单号，追溯码，条目创建时间来生成唯一标识
        /// </summary>
        /// <param name="orderId"></param>
        /// <param name="traceId"></param>
        /// <param name="createTime"></param>
        /// <returns></returns>
        public static string GetHashCodeFromPushMes(string str1, string str2, DateTime dateTime)
        {
            // 1. 将DateTime转换为精确到100纳秒的ISO 8601格式字符串
            string isoTime = dateTime.ToString("yyyy-MM-ddTHH:mm:ss");

            // 2. 拼接所有参数（注意固定顺序）
            string combined = $"{str1}||{str2}||{isoTime}";

            // 3. 使用SHA-256哈希算法
            using (SHA256 sha256 = SHA256.Create())
            {
                byte[] bytes = Encoding.UTF8.GetBytes(combined);
                byte[] hashBytes = sha256.ComputeHash(bytes);

                // 4. 将哈希结果转换为十六进制字符串
                StringBuilder sb = new StringBuilder();
                foreach (byte b in hashBytes)
                {
                    sb.Append(b.ToString("x2")); // 小写十六进制
                }
                return sb.ToString();
            }
        }

        /// <summary>
        /// 分组数组，获取所有分组的下标
        /// </summary>
        /// <param name="array"></param>
        /// <returns></returns>
        public static Dictionary<string, List<string>> GetArrayIndex(string[] array)
        {
            // 初始化分类数据结构
            Dictionary<string, List<string>> dic = new Dictionary<string, List<string>>();

            for (global::System.Int32 i = 0; i < array.Length; i++)
            {
                if (!dic.ContainsKey(array[i]))
                {
                    dic.Add(array[i], new List<string>
                            {
                                i.ToString()
                            });
                }
                else
                {
                    List<string> list = dic[array[i]];

                    list.Add(i.ToString());

                    dic[array[i]] = list;
                }
            }

            return dic;
        }

        /// <summary>
        /// 转速转换角度
        /// </summary>
        /// <param name="stepSpeed"></param>
        /// <returns></returns>
        public static double CalculateStepAngle(List<string> stepSpeed)
        {
            // 转换字符串为数值并过滤无效数据
            List<double> speeds = stepSpeed
                .Select(s => double.TryParse(s, out double val) ? val : 0)
                .ToList();

            // 初始化角度计算变量
            double totalAngle = 0;
            double prevSpeed = 0;
            const double timeInterval = 0.002; // 2ms采样间隔
            const double rpmToDegPerSec = 6;   // 1 rpm = 6 °/s

            // 遍历所有转速点（从第二个点开始）
            for (int i = 0; i < speeds.Count; i++)
            {
                double currentSpeed = speeds[i];

                // 第一个点特殊处理
                if (i == 0)
                {
                    prevSpeed = currentSpeed;
                    continue;
                }

                // 使用梯形法计算角度增量
                double avgSpeed = (prevSpeed + currentSpeed) / 2.0;
                double angleIncrement = avgSpeed * timeInterval * rpmToDegPerSec;

                // 累加角度
                totalAngle += angleIncrement;
                prevSpeed = currentSpeed;
            }

            return Math.Round(totalAngle, 2); // 保留1位小数
        }

        /// <summary>
        /// 获取PLC状态数组
        /// </summary>
        /// <param name="plcName"></param>
        public static (byte[], List<string>) GetStatusBytes(PlcOrderConfigModel orderModel)
        {
            // 初始化状态字段
            string stationStatus = $"{orderModel.PlcName}-STATUS";

            // 获取Plc信号数组
            Tuple<int, int> byteTuple = MappingTable.GetBytesOff(stationStatus);

            if (byteTuple == null)
                return (null, null);

            // 读取状态数组数据
            byte[] bytes = orderModel.plc.ReadBytes(DataType.DataBlock, byteTuple.Item1, SystemConstant.ALL_STARTOFF, byteTuple.Item2);

            // 获取状态字段对应顺序
            List<string> stationList = MappingTable.GetDicByName(stationStatus);

            return (bytes, stationList);
        }

        /// <summary>
        /// 是默认时间返回true，现在时间false
        /// </summary>
        /// <param name="endTime"></param>
        /// <returns></returns>
        internal static bool IsDateTimeValid(DateTime? endTime)
        {
            return !(endTime.HasValue && endTime.Value.Year > 1990);
        }

        /// <summary>
        /// 计算报警时间差
        /// </summary>
        /// <param name="startTime"></param>
        /// <param name="endTime"></param>
        /// <returns></returns>
        public static string CalculateTimeDifferenceInMinutes(DateTime startTime, DateTime endTime)
        {
            // 计算两个时间点的时间差
            TimeSpan timeSpan = endTime - startTime;

            // 如果时间差为负，说明结束时间早于开始时间，返回0
            if (timeSpan.TotalMinutes < 0)
            {
                return "0";
            }

            // 获取时间差的分钟数，向上取整
            int minutes = (int)Math.Ceiling(timeSpan.TotalMinutes);

            // 如果分钟数小于1，返回1分钟
            return (minutes < 1 ? 1 : minutes).ToString();
        }

        /// <summary>
        /// 大端转小端转INT转FLOAT
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        internal static float SmallToFloat(byte[] bytes)
        {
            try
            {
                if (BitConverter.IsLittleEndian)
                {
                    Array.Reverse(bytes);
                }

                int intValue = BitConverter.ToInt32(bytes, 0);

                float value = (float)intValue;

                return value;
            }
            catch (Exception e)
            {
                ProduceLogTextError($"!!字节转换错误: {e.Message} | " +
                                         $"输入长度: {bytes?.Length ?? 0} | " +
                                         $"内容: {BitConverter.ToString(bytes ?? new byte[0])}");
                return 0;
            }
        }

        public static OrderStatus GetOrder(string station, string code, OrderDao orderDao)
        {
            // 首工序工单从系统缓存取，其余从数据库取
            OrderStatus order = null;

            if (MappingTable.GetStartStation(station))
            {
                OrderSingletonModel orderSingletonModel = OrderSingleton.GetOrderId(station);

                if (orderSingletonModel != null)
                    order = new OrderStatus()
                    {
                        OrderId = orderSingletonModel.OrderId,
                        OrderIssuedTime = orderSingletonModel.OrderCreateTime
                    };
            }
            else
            {
                List<OrderTrace> orderTrace = orderDao.GetOrderIdFromOrderTrace(code);

                // 取出工单号
                List<string> idList = orderTrace.Select(x => x.OrderId).ToList();

                // 从工单表中取出数据
                List<OrderStatus> orderStatuses = orderDao.GetOrder(idList);

                if (station.Equals(SystemConstant.R3) || station.Equals(SystemConstant.R4) || station.Equals(SystemConstant.R5))
                {
                    station = code.Contains("LAD-80010050") ? station + "L" : station + "R";

                    // Tools.ProduceLogTextError($"触发！[{partProcess.Station}][{partProcess.TraceId}]");
                }

                // 取出工位对应物料编码
                string materialId = MappingTable.GetMaterialByStation(station);

                OrderStatus orderStatus = orderStatuses.Where(x => x.MaterialId == materialId).FirstOrDefault();

                if (orderStatus != null)
                    order = orderStatus;
            }

            return order;
        }

        /// <summary>
        /// 大端转小端转INT转FLOAT
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        internal static float SmallToInt(byte[] bytes)
        {
            try
            {
                if (BitConverter.IsLittleEndian)
                {
                    Array.Reverse(bytes);
                }

                return BitConverter.ToInt32(bytes, 0);
            }
            catch (Exception e)
            {
                ProduceLogTextError($"!!字节转换错误: {e.Message} | " +
                                         $"输入长度: {bytes?.Length ?? 0} | " +
                                         $"内容: {BitConverter.ToString(bytes ?? new byte[0])}");
                return 0;
            }
        }


        #endregion


    }
}
